package physics;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import fiziksobject.FiziksBall;
import gui.Main;

public class PhysicsEngine implements Runnable, Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static PhysicsEngine getPhysicsEngine() {
		if (pe == null) {
			pe = new PhysicsEngine();
			Thread t = new Thread(pe);
			t.start();
		}
		return pe;
	}
	public static void setPhysicsEngine(PhysicsEngine readObject) {
		pe.die();
		pe = readObject;
		Thread t = new Thread(pe);
		t.start();
	}
	private static PhysicsEngine pe;

	private ArrayList<Forcer> forcers;
	private ArrayList<physicsobject> physObjs;
	public static final double TIMESTEP = 0.001;
	private boolean isRunning;
	private boolean die=false;
	public void die() {
		die = true;
	}
	private boolean stableCode = true;

	private PhysicsEngine() {
		this.forcers = new ArrayList<Forcer>();
		this.physObjs = new ArrayList<physicsobject>();
		this.isRunning = false;
	}

	public ArrayList<Forcer> getForcers() {
		return (ArrayList<Forcer>) forcers.clone();
	}

	public void addForcer(Forcer f) {
		forcers.add(f);
	}

	public void removeForcer(Forcer f) {
		forcers.remove(f);
	}

	public void removePhysicsObject(physicsobject po) {
		physObjs.remove(po);
	}

	public void addPhysicsObject(physicsobject po) {
		physObjs.add(po);
	}

	public void startEngine() {
		isRunning = true;
	}

	public void pauseEngine() {
		isRunning = false;
	}

	public boolean isRunning() {
		return isRunning;
	}

	public void run() {
		while(!die) {
			try {
				if (isRunning) {
					doTimeStep();
				}
				Thread.sleep((long) (TIMESTEP * 1000d));
			} catch (Exception e) {
				e.printStackTrace();
			}
			Main.j.repaint();
		}
	}

	private void doTimeStep() {
		ArrayList<Forcer> forcers = getForcers();

		ArrayList<physicsobject> physObjs = getPhysicsObjects();
		for (physicsobject po : physObjs) {
			for (Forcer f : forcers) {
				Vector[] forces = f.getForces(po);
				for (Vector v : forces) {
					po.applyForce(v);
				}
			}
		}
		//shiftIntersections();

		for (physicsobject i : physObjs) {
			for (physicsobject j : physObjs) {
				// System.out.println("Getting force");
				if (i != j) {
					Vector[] forces = i.getForces(j);
					for (Vector force : forces) {
						j.applyForce(force);

					}
				}

			}
		}/*
		for (physicsobject i : physObjs) {
			i.updateMomentum();
		}*/
		// WIERD SHIT
		if (stableCode) {
			for (physicsobject i : physObjs) {
				for (physicsobject j : physObjs) {
					// System.out.println("Getting force");
					if (i != j) {
						j.applyForce(i.getNormalForce3(j));
					}

				}
			}
		}
		for (physicsobject po : physObjs) {
			po.updateLocation();
		}
		// total energy

		double energy = 0;
		double momentum = 0;
		for (physicsobject po : physObjs) {
			// kinetic
			energy += 0.5 * Math.pow(po.getMomentum().getNetMag(), 2)
					/ po.getMass();
			momentum += po.getMomentum().getNetMag();

			// potential
			// energy += po.getMass()*po.getLoc().getY()*9.834;
		}
		// System.out.println(momentum);
		System.out.println(energy);
	}

	public boolean isStableCode() {
		return stableCode;
	}

	public void setIsStableCode(boolean b) {
		stableCode = b;
	}

	public ArrayList<physicsobject> getPhysicsObjects() {
		return (ArrayList<physicsobject>) physObjs.clone();
	}

	public void clearPhysicsObjects() {
		physObjs = new ArrayList<physicsobject>();
	}

	public void shiftIntersections() {
		/*
		for (int i = 0; i < physObjs.size(); i++) {
			for (int j = i; j < physObjs.size(); j++) {
				if (i != j) {
					if (physObjs.get(i).isColliding(physObjs.get(j))) {
						FiziksBall f2 = (FiziksBall) physObjs.get(j);
						FiziksBall f1 = (FiziksBall) physObjs.get(i);
						double mratio = f1.getMass() / f2.getMass();
						double distance = f1.getRadius() + f2.getRadius();
						double offNess = distance
								- f1.getLoc().distanceTo(f2.getLoc());
						if (mratio <= 1) {
							mratio = 1 / mratio;
							double theta = Math.atan2(f1.getLoc().getY()
									- f2.getLoc().getY(), f1.getLoc().getX()
									- f2.getLoc().getX());
							f1.setLoc(new Location(
									f1.getLoc().getX() + (1 - mratio)
											* offNess * Math.cos(theta), f1
											.getLoc().getY()
											+ (1 - mratio)
											* offNess
											* Math.sin(theta)));
							f2.setLoc(new Location(f2.getLoc().getX() - mratio
									* offNess * Math.cos(theta), f2.getLoc()
									.getY()
									- mratio * offNess * Math.sin(theta)));
						} else {
							double theta = Math.atan2(f1.getLoc().getY()
									- f2.getLoc().getY(), f1.getLoc().getX()
									- f2.getLoc().getX());
							f2.setLoc(new Location(
									f2.getLoc().getX() - (1 - mratio)
											* offNess * Math.cos(theta), f2
											.getLoc().getY()
											- (1 - mratio)
											* offNess
											* Math.sin(theta)));
							f1.setLoc(new Location(f2.getLoc().getX() + mratio
									* offNess * Math.cos(theta), f1.getLoc()
									.getY()
									+ mratio * offNess * Math.sin(theta)));
						}
					}
				}
				
			}
		}*/
	}
}
